package Kruskal;

import java.util.Arrays;

public class KruskalCase {

    //边的条数
    private int edgeNum;
    //节点的值
    private char[] vertexs;
    //邻接矩阵
    private int[][] martix;
    //两个顶点无法联通
    private static final int INF = Integer.MAX_VALUE;

    public static void main(String[] args) {
        char[] vertexs = {'A','B','C','D','E','F','G'};
        int[][] martix = {
                /*A*/ {   0,  12, INF, INF, INF,  16,  14},
                /*B*/ {  12,   0,  10, INF, INF,   7, INF},
                /*C*/ { INF,  10,   0,   3,   5,   6, INF},
                /*D*/ { INF, INF,   3,   0,   4, INF, INF},
                /*E*/ { INF, INF,   5,   4,   0,   2,   8},
                /*F*/ {  16,   7,   6, INF,   2,   0,   9},
                /*G*/ {  14, INF, INF, INF,   8,   9,   0}
        };
        KruskalCase kruskalCase = new KruskalCase(vertexs,martix);
        kruskalCase.print();

        EData[] edges = kruskalCase.getEdges();
        kruskalCase.sortEdge(edges);
        System.out.println(Arrays.toString(edges));
        kruskalCase.kruskal();
    }

    //构造器

    public KruskalCase(char[] vertexs, int[][] martix) {
        int vlen = vertexs.length;

        this.vertexs = new char[vlen];
        for(int i = 0; i < vlen; i++){
            this.vertexs[i] = vertexs[i];
        }

        this.martix = new int[vlen][vlen];
        for(int i = 0; i < vlen; i++){
            for(int j = 0; j < vlen; j++){
                this.martix[i][j] = martix[i][j];
            }
        }

        for(int i = 0; i < vlen; i++){
            for(int j = i + 1; j < vlen; j++){
                if(martix[i][j] != INF){
                    edgeNum++;
                }
            }
        }
    }

    //打印邻接矩阵
    public void print(){
        System.out.println("邻接矩阵为：");
        for(int i = 0; i < vertexs.length; i++){
            for(int j = 0; j < vertexs.length; j++){
                System.out.printf("%15d", martix[i][j]);
            }
            System.out.println();
        }
    }

    public void kruskal(){
        int index = 0;
        //用于保存"已有最小生成树" 中的每个顶点在最小生成树中的终点
        int[] ends = new int[edgeNum];
        //最小生成树
        EData[] rets = new EData[edgeNum];
        //获取图中 所有的边的集合 ， 一共有12边
        EData[] edges = getEdges();
        //排序
        sortEdge(edges);

        //遍历edges 数组，将边添加到最小生成树中时，判断是准备加入的边否形成了回路，
        //如果没有，就加入 rets, 否则不能加入
        for(int i = 0; i < edgeNum; i++){
            //获取到第i条边的第一个顶点(起点)
            int p1 = getPosition(edges[i].start);
            //获取到第i条边的第2个顶点
            int p2 = getPosition(edges[i].end);
            //获取p1这个顶点在已有最小生成树中的终点
            int m = getEnd(ends,p1);
            //获取p2这个顶点在已有最小生成树中的终点
            int n = getEnd(ends,p2);
            //是否构成回路
            if(m != n){
                //没有构成回路，设置m 在"已有最小生成树"中的终点
                ends[m] = n;
                //有一条边加入到rets数组
                rets[index++] = edges[i];
            }
        }
        System.out.println("最小生成树为");
        for(int i = 0; i < index; i++) {
            System.out.println(rets[i]);
        }
    }

    /**
     * 对边的权值进行冒泡排序
     * @param edge:传入的边
     */
    public void sortEdge(EData[] edge){
        EData temp = null;
        for(int i = 0; i < edge.length - 1; i++){
            for(int j = 0; j < edge.length - 1 - i; j++){
                if(edge[j].weight > edge[j+1].weight){
                    temp = edge[j+1];
                    edge[j+1] = edge[j];
                    edge[j] = temp;
                 }
            }
        }
    }

    /**
     * 找到ch顶点对应的下标
     * @param ch:需要确定位置的顶点
     * @return:顶点对应的下标
     */
    public int getPosition(char ch){
        for(int i = 0; i < vertexs.length; i++){
            if(vertexs[i] == ch){
                return i;
            }
        }
        return -1;
    }

    public EData[] getEdges(){
        int index = 0;
        EData[] edges = new EData[edgeNum];

        for(int i = 0; i < vertexs.length; i++){
            for(int j = i + 1; j < vertexs.length; j++){
                if(martix[i][j] != INF){
                    edges[index++] = new EData(vertexs[i],vertexs[j],martix[i][j]);
                }
            }
        }
        return edges;
    }

    /**
     * 功能: 获取下标为i的顶点的终点(), 用于后面判断两个顶点的终点是否相同
     * @param ends ： 数组就是记录了各个顶点对应的终点是哪个,ends 数组是在遍历过程中，逐步形成
     * @param i : 表示传入的顶点对应的下标
     * @return 返回的就是 下标为i的这个顶点对应的终点的下标, 一会回头还有来理解
     */
    public int getEnd(int[] ends,int i){
        while(ends[i] != 0){
            i = ends[i];
        }
        return i;
    }

}

class EData{
    char start;
    char end;
    int weight;

    //构造器

    public EData(char start, char end, int weight) {
        this.start = start;
        this.end = end;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "EData{" +
                "start=" + start +
                ", end=" + end +
                ", weight=" + weight +
                '}';
    }
}



